/**
 * Copyright (c) 2006 Amgad S Youssef
 * All Rights Reserved.
 * Licensed under the Eclipse Public License - v 1.0
 * For more information see http://www.eclipse.org/legal/epl-v10.html
 */
package org.eclipsejcde.core;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Hashtable;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;

import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaModel;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeHierarchy;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.ToolFactory;
import org.eclipse.jdt.core.formatter.CodeFormatter;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.jface.preference.PreferenceDialog;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.text.edits.TextEdit;
import org.eclipse.ui.dialogs.PreferencesUtil;
import org.eclipsejcde.core.launchers.CREFLauncher;
import org.eclipsejcde.core.launchers.ILauncher;
import org.eclipsejcde.core.launchers.JCToolLauncher;
import org.eclipsejcde.core.launchers.JCWDELauncher;
import org.eclipsejcde.core.launchers.arguments.ArgumentsPreparer;
import org.eclipsejcde.core.preferences.JCPreferences;

/**
 * @author Amgad S Youssef
 * 
 */
public class JCDECore {
	public static final String NATURE_ID = "org.eclipsejcde.core.javacardNature";

	public static final String BUILDER_ID = "org.eclipsejcde.core.javacardBuilder";

	public static final String MARKER_TYPE = "org.eclipsejcde.core.problem";

	public static final String PROJECT_PROPERTIES_FILE_NAME = ".jcproject";

	public static final String JCDK_CONTAINER_PATH = "JCDK2.2.2";

	public static final String[] SUPPORTED_JCDK_VERSIONS = new String[] { "Java Card 2.2.1","Java Card 2.2.2" };

	private static String currentJCDKVersion;

	private static String currentJCDKHome;

	private static CREFLauncherThread crefLauncherThread;

	public static String getCurrentJCDKVersion() {
		if (currentJCDKHome == null || currentJCDKHome.equals("")) {
			currentJCDKHome = JCPreferences.getJAVA_CARD_PATH();
			IStatus status = validateCurrentJCDKHomeCompatibility();
			if (status.getSeverity() != IStatus.OK) {
				//MessageDialog.openError(new Shell(), "JCDK Home", "Please go to Java Card preferences and select a compatible JCDK home location.");
				PreferenceDialog pd = PreferencesUtil.createPreferenceDialogOn(new Shell(), "org.eclipsejcde.preferences.pages.JCHomeLocationPage", new String[] {
						"org.eclipsejcde.preferences.pages.JCHomeLocationPage", "org.eclipsejcde.preferences.pages.CREFPreferences" }, null);
				if (pd.open() == Window.OK) {
				}
			}
		}
		return currentJCDKVersion;
	}

	public static String getCurrentJCDKHome() {
		if (currentJCDKHome == null || currentJCDKHome.equals("")) {
			currentJCDKHome = JCPreferences.getJAVA_CARD_PATH();
			IStatus status = validateCurrentJCDKHomeCompatibility();
			if (status.getSeverity() != IStatus.OK) {
				//MessageDialog.openError(new Shell(), "JCDK Home", "Please go to Java Card preferences and select a compatible JCDK home location.");
				PreferenceDialog pd = PreferencesUtil.createPreferenceDialogOn(new Shell(), "org.eclipsejcde.preferences.pages.JCHomeLocationPage", new String[] {
						"org.eclipsejcde.preferences.pages.JCHomeLocationPage", "org.eclipsejcde.preferences.pages.CREFPreferences" }, null);
				if (pd.open() == Window.OK) {
				}
			}
		}
		return currentJCDKHome;
	}

	public static IStatus validateCurrentJCDKHomeCompatibility() {
		return validateJCDKHomeCompatibility(currentJCDKHome);
	}

	public static IStatus validateJCDKHomeCompatibility(String jcdkHomeLocation) {
		String pSep = File.separator;
		if (jcdkHomeLocation == null || jcdkHomeLocation.equals("")) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, "JCDK home can not be empty.", null);
		}

		// validate the existence of the entered/selected directory
		File jcHome = new File(jcdkHomeLocation);
		if (!jcHome.exists()) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, "JCDK home is not valid.", null);
		}
		// validate the existence of the lib directory
		File jcHomeLib = new File(jcdkHomeLocation + pSep + "lib");
		if (!jcHomeLib.exists()) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, "lib directory could not be found in JCDK home.", null);
		}

		// validate the existence of the api_export_files directory
		File jcHomApiExportFiles = new File(jcdkHomeLocation + pSep + "api_export_files");
		if (!jcHomApiExportFiles.exists()) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, "api_export_files directory could not be found in JCDK home.", null);
		}

		// validate compatibility of all tools (converter, apduio, capdump,
		// jcwde, offcardverifier, scriptgen, !jcwde_16)

		String tempCurrentJCDKVersion = null;
		// converter
		File converter = new File(jcdkHomeLocation + pSep + "lib" + pSep + "converter.jar");
		if (!converter.exists()) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, "converter.jar could not be found in lib directory.", null);
		}
		IStatus status = validateJCDKToolCompatibility(converter);
		if (status.getSeverity() == IStatus.ERROR) {
			return status;
		} else if (status.getSeverity() == IStatus.OK) {
			tempCurrentJCDKVersion = status.getMessage();
		}

		// apduio
		File apduio = new File(jcdkHomeLocation + pSep + "lib" + pSep + "apduio.jar");
		if (!apduio.exists()) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, "apduio.jar could not be found in lib directory.", null);
		}
		status = validateJCDKToolCompatibility(apduio);
		if (status.getSeverity() == IStatus.ERROR) {
			return status;
		} else if (status.getSeverity() == IStatus.OK) {
			if (!status.getMessage().equals(tempCurrentJCDKVersion)) {
				return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, "jars in the lib directory are not all of the same JCDK version.", null);
			}
		}

		// capdump
		File capdump = new File(jcdkHomeLocation + pSep + "lib" + pSep + "capdump.jar");
		if (!capdump.exists()) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, "capdump.jar could not be found in lib directory.", null);
		}
		status = validateJCDKToolCompatibility(capdump);
		if (status.getSeverity() == IStatus.ERROR) {
			return status;
		} else if (status.getSeverity() == IStatus.OK) {
			if (!status.getMessage().equals(tempCurrentJCDKVersion)) {
				return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, "jars in the lib directory are not all of the same JCDK version.", null);
			}
		}

		// jcwde
		File jcwde = new File(jcdkHomeLocation + pSep + "lib" + pSep + "jcwde.jar");
		if (!jcwde.exists()) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, "jcwde.jar could not be found in lib directory.", null);
		}
		status = validateJCDKToolCompatibility(jcwde);
		if (status.getSeverity() == IStatus.ERROR) {
			return status;
		} else if (status.getSeverity() == IStatus.OK) {
			if (!status.getMessage().equals(tempCurrentJCDKVersion)) {
				return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, "jars in the lib directory are not all of the same JCDK version.", null);
			}
		}

		// offcardverifier
		File offcardverifier = new File(jcdkHomeLocation + pSep + "lib" + pSep + "offcardverifier.jar");
		if (!offcardverifier.exists()) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, "offcardverifier.jar could not be found in lib directory.", null);
		}
		status = validateJCDKToolCompatibility(offcardverifier);
		if (status.getSeverity() == IStatus.ERROR) {
			return status;
		} else if (status.getSeverity() == IStatus.OK) {
			if (!status.getMessage().equals(tempCurrentJCDKVersion)) {
				return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, "jars in the lib directory are not all of the same JCDK version.", null);
			}
		}

		// scriptgen
		File scriptgen = new File(jcdkHomeLocation + pSep + "lib" + pSep + "scriptgen.jar");
		if (!scriptgen.exists()) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, "scriptgen.jar could not be found in lib directory.", null);
		}
		status = validateJCDKToolCompatibility(scriptgen);
		if (status.getSeverity() == IStatus.ERROR) {
			return status;
		} else if (status.getSeverity() == IStatus.OK) {
			if (!status.getMessage().equals(tempCurrentJCDKVersion)) {
				return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, "jars in the lib directory are not all of the same JCDK version.", null);
			}
		}

		// // jcwde_16
		// File jcwde_16 = new File(jcdkHomeLocation + pSep + "lib" + pSep +
		// "jcwde_16.jar");
		// if (!jcwde_16.exists()) {
		// return new Status(Status.ERROR, Activator.PLUGIN_ID, Status.OK,
		// "jcwde_16.jar could not be found in lib directory.", null);
		// }
		// status = validateJCDKToolCompatibility(jcwde_16);
		// if (status.getSeverity() == IStatus.ERROR) {
		// return status;
		// } else if (status.getSeverity() == IStatus.OK) {
		// if (!status.getMessage().equals(tempCurrentJCDKVersion)) {
		// return new Status(Status.ERROR, Activator.PLUGIN_ID, Status.OK, "jars
		// in the lib directory are not all of the same JCDK version.", null);
		// }
		// }

		currentJCDKVersion = tempCurrentJCDKVersion;
		currentJCDKHome = jcdkHomeLocation;
		return new Status(IStatus.OK, Activator.PLUGIN_ID, IStatus.OK, jcdkHomeLocation + " is compatible.", null);
	}

	public static IStatus validateJCDKToolCompatibility(String jcdkToolLocation) {
		File jcdkTool = new File(jcdkToolLocation);
		return validateJCDKToolCompatibility(jcdkTool);
	}

	public static IStatus validateJCDKToolCompatibility(File jcdkToolFile) {
		try {
			if (jcdkToolFile == null || !jcdkToolFile.exists()) {
				Status status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, "\"" + jcdkToolFile.getAbsolutePath() + "\" does not exist.", null);
				return status;
			}
			// Convert File to a URL
			URL url = jcdkToolFile.toURI().toURL();
			URL[] urls = new URL[] { url };

			// Create a new class loader with the directory
			ClassLoader cl = new URLClassLoader(urls);

			// Load in the class
			Class<?> cls = cl.loadClass("com.sun.javacard.ToolsVersion");
			Method method = cls.getMethod("getProductName", new Class[] {});
			String productName = (String) method.invoke(cls.newInstance(), new Object[] {});
			for (int i = 0; i < SUPPORTED_JCDK_VERSIONS.length; i++) {
				if (productName.indexOf(SUPPORTED_JCDK_VERSIONS[i]) > -1) {
					return new Status(IStatus.OK, Activator.PLUGIN_ID, IStatus.OK, SUPPORTED_JCDK_VERSIONS[i], null);
				}
			}
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, jcdkToolFile.getName() + " is not compatible.", null);
		} catch (Exception e) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, e.getMessage(), e);
		}
	}

	public static IStatus startCREF(String outputFileLocation, String[] options) {
		if (outputFileLocation != null && outputFileLocation.equals("")) {
			File file = new File(outputFileLocation);
			return CREFLauncher.getLauncher()
					.launch(ArgumentsPreparer.prepareCREFArguments(outputFileLocation, options), "CREF [" + file.getName() + "]", ILauncher.DELETE_MODE_DELETE_AFTER_LAUNCH);
		} else {
			return CREFLauncher.getLauncher().launch(ArgumentsPreparer.prepareCREFArguments(outputFileLocation, options), "CREF", ILauncher.DELETE_MODE_DELETE_AFTER_LAUNCH);
		}
	}

	public static IStatus startCREF(String outputFileLocation, String[] options, boolean keepRunning) {
		if (keepRunning) {
			crefLauncherThread = new CREFLauncherThread(outputFileLocation, options);
			crefLauncherThread.start();
			return new Status(IStatus.OK, Activator.PLUGIN_ID, IStatus.OK, "CREF launcher thread started and will keep running", null);
		} else {
			return startCREF(outputFileLocation, options);
		}
	}

	public static IStatus stopCREF() {
		if (crefLauncherThread != null && !crefLauncherThread.isTerminated()) {
			crefLauncherThread.terminate();
			crefLauncherThread = null;
			return new Status(IStatus.OK, Activator.PLUGIN_ID, IStatus.OK, "CREF launcher thread stopped", null);
		}
		return CREFLauncher.getLauncher().terminate();
	}

	public static IStatus startJCWDE(ICompilationUnit[] jcApplets, String[] options, boolean inDebugMode) {
		try {
			String configFileLocation = ResourcesPlugin.getPlugin().getStateLocation().toFile().getAbsolutePath() + File.separator + "jcwde.config";
			File configFile = new File(configFileLocation);
			if (configFile.exists()) {
				configFile.delete();
			}
			configFile.createNewFile();
			PrintWriter pw = new PrintWriter(configFile);
			pw.println("com.sun.javacard.installer.InstallerApplet 0xa0:0x0:0x0:0x0:0x62:0x3:0x1:0x8:0x1");

			TreeMap<String, IJavaProject> classpathProjectsMap = new TreeMap<String, IJavaProject>();
			for (int i = 0; i < jcApplets.length; i++) {
				IStatus status = getAppletAID(jcApplets[i]);
				if (status.getSeverity() != IStatus.OK) {
					return status;
				}
				String appletAID = status.getMessage();
				String appletFullyQualifiedName = jcApplets[i].findPrimaryType().getFullyQualifiedName();
				pw.println(appletFullyQualifiedName + " " + appletAID);
				classpathProjectsMap.put(jcApplets[i].getJavaProject().getElementName(), jcApplets[i].getJavaProject());
				String[] projectNames = jcApplets[i].getJavaProject().getRequiredProjectNames();
				for (int j = 0; j < projectNames.length; j++) {
					IJavaProject javaProject = JavaCore.create(ResourcesPlugin.getWorkspace().getRoot().getProject(projectNames[j]));
					classpathProjectsMap.put(javaProject.getElementName(), javaProject);
				}
			}
			pw.close();
			Collection<IJavaProject> values = classpathProjectsMap.values();
			IJavaProject[] classpathProjects = new IJavaProject[values.size()];
			values.toArray(classpathProjects);
			return launchJCWDE(configFileLocation, classpathProjects, options, inDebugMode);
		} catch (Exception e) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, e.getMessage(), e);
		}
	}

	public static IStatus stopJCWDE() {
		return JCWDELauncher.terminateJCWDE();
	}

	public static IStatus setPackageAID(IPackageFragment packageFragment, String packageAID) {
		IStatus status = validateAID(packageAID);
		if (status.getSeverity() != IStatus.OK) {
			return status;
		}
		try {
			Properties packageProps = new Properties();
			File jcProjectProperties = packageFragment.getJavaProject().getResource().getLocation().append(File.separator + PROJECT_PROPERTIES_FILE_NAME).toFile();
			if (jcProjectProperties.exists()) {
				packageProps.loadFromXML(new FileInputStream(jcProjectProperties));
			} else {
				jcProjectProperties.createNewFile();
			}
			packageProps.setProperty(packageFragment.getElementName(), packageAID);
			packageProps.storeToXML(new FileOutputStream(jcProjectProperties), "Java Card project properties");
			return new Status(IStatus.OK, Activator.PLUGIN_ID, IStatus.OK, "\"" + packageFragment.getElementName() + "\" package AID set successfully.", null);
		} catch (Exception e) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, e.getMessage(), e);
		}
	}

	public static IStatus getPackageAID(IPackageFragment packageFragment) {
		try {
			File jcProjectProperties = packageFragment.getJavaProject().getResource().getLocation().append(File.separator + PROJECT_PROPERTIES_FILE_NAME).toFile();
			if (!jcProjectProperties.exists()) {
				return new Status(IStatus.WARNING, Activator.PLUGIN_ID, IStatus.OK, packageFragment.getElementName() + " has no Package AID.", null);
			}
			Properties packageProps = new Properties();
			packageProps.loadFromXML(new FileInputStream(jcProjectProperties));
			String packageAID = packageProps.getProperty(packageFragment.getElementName());
			if (packageAID == null || packageAID.equals("")) {
				return new Status(IStatus.WARNING, Activator.PLUGIN_ID, IStatus.OK, packageFragment.getElementName() + " has no Package AID.", null);
			}
			return new Status(IStatus.OK, Activator.PLUGIN_ID, IStatus.OK, packageAID, null);
		} catch (Exception e) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, e.getMessage(), e);
		}
	}

	public static IStatus removePackageAID(IPackageFragment packageFragment) {
		try {
			File jcProjectProperties = packageFragment.getJavaProject().getResource().getLocation().append(File.separator + PROJECT_PROPERTIES_FILE_NAME).toFile();
			if (!jcProjectProperties.exists()) {
				return new Status(IStatus.WARNING, Activator.PLUGIN_ID, IStatus.OK, packageFragment.getElementName() + " Package AID does not exist.", null);
			}
			Properties packageProps = new Properties();
			packageProps.loadFromXML(new FileInputStream(jcProjectProperties));
			if (packageProps.getProperty(packageFragment.getElementName()) == null) {
				return new Status(IStatus.WARNING, Activator.PLUGIN_ID, IStatus.OK, packageFragment.getElementName() + " Package AID does not exist.", null);
			}
			packageProps.remove(packageFragment.getElementName());
			packageProps.storeToXML(new FileOutputStream(jcProjectProperties), "Java Card project properties");
			return new Status(IStatus.OK, Activator.PLUGIN_ID, IStatus.OK, "\"" + packageFragment.getElementName() + "\" package AID removed successfully.", null);
		} catch (Exception e) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, e.getMessage(), e);
		}
	}

	public static IStatus setAppletAID(ICompilationUnit compilationUnit, String appletAID) {
		IStatus status = validateAID(appletAID);
		if (status.getSeverity() != IStatus.OK) {
			return status;
		}
		try {
			Properties packageProps = new Properties();
			File jcProjectProperties = compilationUnit.getParent().getJavaProject().getResource().getLocation().append(File.separator + PROJECT_PROPERTIES_FILE_NAME).toFile();
			if (jcProjectProperties.exists()) {
				packageProps.loadFromXML(new FileInputStream(jcProjectProperties));
			} else {
				jcProjectProperties.createNewFile();
			}
			IType type = compilationUnit.findPrimaryType();
			packageProps.setProperty(type.getFullyQualifiedName(), appletAID);
			packageProps.storeToXML(new FileOutputStream(jcProjectProperties), "Java Card project properties");
			return new Status(IStatus.OK, Activator.PLUGIN_ID, IStatus.OK, "\"" + type.getFullyQualifiedName() + "\" applet AID set successfully.", null);
		} catch (Exception e) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, e.getMessage(), e);
		}
	}

	public static IStatus getAppletAID(ICompilationUnit compilationUnit) {
		try {
			File jcProjectProperties = compilationUnit.getParent().getJavaProject().getResource().getLocation().append(File.separator + PROJECT_PROPERTIES_FILE_NAME).toFile();
			if (!jcProjectProperties.exists()) {
				return new Status(IStatus.WARNING, Activator.PLUGIN_ID, IStatus.OK, compilationUnit.getElementName() + " has not Applet AID.", null);
			}
			Properties packageProps = new Properties();
			packageProps.loadFromXML(new FileInputStream(jcProjectProperties));
			IType type = compilationUnit.findPrimaryType();
			String appletAID = packageProps.getProperty(type.getFullyQualifiedName());
			if (appletAID == null || appletAID.equals("")) {
				return new Status(IStatus.WARNING, Activator.PLUGIN_ID, IStatus.OK, compilationUnit.getElementName() + " has not Applet AID.", null);
			}
			return new Status(IStatus.OK, Activator.PLUGIN_ID, IStatus.OK, appletAID, null);
		} catch (Exception e) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, e.getMessage(), e);
		}
	}

	public static IStatus removeAppletAID(ICompilationUnit compilationUnit) {
		try {
			File jcProjectProperties = compilationUnit.getParent().getJavaProject().getResource().getLocation().append(File.separator + PROJECT_PROPERTIES_FILE_NAME).toFile();
			if (!jcProjectProperties.exists()) {
				return new Status(IStatus.WARNING, Activator.PLUGIN_ID, IStatus.OK, compilationUnit.getElementName() + " has not Applet AID.", null);
			}
			Properties packageProps = new Properties();
			packageProps.loadFromXML(new FileInputStream(jcProjectProperties));
			IType type = compilationUnit.findPrimaryType();
			if (packageProps.getProperty(type.getFullyQualifiedName()) == null) {
				return new Status(IStatus.WARNING, Activator.PLUGIN_ID, IStatus.OK, compilationUnit.getElementName() + " has not Applet AID.", null);
			}
			packageProps.remove(type.getFullyQualifiedName());
			packageProps.storeToXML(new FileOutputStream(jcProjectProperties), "Java Card project properties");
			return new Status(IStatus.OK, Activator.PLUGIN_ID, IStatus.OK, "\"" + compilationUnit.getElementName() + "\" applet AID removed successfully.", null);
		} catch (Exception e) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, e.getMessage(), e);
		}
	}

	public static IStatus validateAID(String aid) {
		String aidFormatMessage = "AID must be 5 to 16 decimal, hexadecimal or octal numbers separated by colons. Each of the numbers must be byte-length.";
		if (aid == null || aid.equals("")) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, "AID must not be empty.", null);
		}
		if (aid.startsWith(":") || aid.endsWith(":")) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, aidFormatMessage, null);
		}
		if (aid.indexOf(':') < 0) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, aidFormatMessage, null);
		}
		String[] aidBytes = aid.split(":");
		if (aidBytes.length < 5 || aidBytes.length > 16) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, aidFormatMessage, null);
		}
		try {
			for (int i = 0; i < aidBytes.length; i++) {
				if (aidBytes[i].startsWith("0x")) {
					String hexStr = aidBytes[i].substring(2);
					if (hexStr.length() < 1) {
						return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, aidFormatMessage, null);
					}
					int decVal = Integer.parseInt(hexStr, 16);
					if (decVal > 255) {
						return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, aidFormatMessage, null);
					}
				} else if (aidBytes[i].startsWith("0")) {
					String octStr = aidBytes[i].substring(1);
					if (octStr.length() < 1) {
						return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, aidFormatMessage, null);
					}
					int decVal = Integer.parseInt(octStr, 8);
					if (decVal > 255) {
						return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, aidFormatMessage, null);
					}
				} else {
					String decStr = aidBytes[i];
					if (decStr.length() < 1) {
						return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, aidFormatMessage, null);
					}
					int decVal = Integer.parseInt(decStr, 10);
					if (decVal > 255) {
						return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, aidFormatMessage, null);
					}
				}
			}
			return new Status(IStatus.OK, Activator.PLUGIN_ID, IStatus.OK, "AID is valid", null);
		} catch (NumberFormatException e) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, aidFormatMessage, null);
		}
	}

	public static int[] parseAID(String aid) {
		if (validateAID(aid).getSeverity() != IStatus.OK) {
			return null;
		}
		String[] aidBytes = aid.split(":");
		int[] aidBytesArr = new int[aidBytes.length];
		try {
			for (int i = 0; i < aidBytes.length; i++) {
				if (aidBytes[i].startsWith("0x")) {
					String hexStr = aidBytes[i].substring(2);
					int decVal = Integer.parseInt(hexStr, 16);
					aidBytesArr[i] = decVal;
				} else if (aidBytes[i].startsWith("0")) {
					String octStr = aidBytes[i].substring(1);
					int decVal = Integer.parseInt(octStr, 8);
					aidBytesArr[i] = decVal;
				} else {
					String decStr = aidBytes[i];
					int decVal = Integer.parseInt(decStr, 10);
					aidBytesArr[i] = decVal;
				}
			}
			return aidBytesArr;
		} catch (NumberFormatException e) {
			return null;
		}
	}

	private static IStatus launchConverter(String javacardHomeLocation, String arguments, String launchConfigurationName) {
		return JCToolLauncher.getLauncher(JCToolLauncher.TOOL_TYPE_CONVERTER).launch(arguments, launchConfigurationName, true, ILauncher.DELETE_MODE_DELETE_AFTER_LAUNCH);
	}

	private static IStatus launchCapgen(String javacardHomeLocation, String arguments, String launchConfigurationName) {
		return JCToolLauncher.getLauncher(JCToolLauncher.TOOL_TYPE_CAPGEN).launch(arguments, launchConfigurationName, true, ILauncher.DELETE_MODE_DELETE_AFTER_LAUNCH);
	}

	private static IStatus launchScriptgen(String javacardHomeLocation, String arguments, String launchConfigurationName) {
		return JCToolLauncher.getLauncher(JCToolLauncher.TOOL_TYPE_SCRIPTGEN).launch(arguments, launchConfigurationName, true, ILauncher.DELETE_MODE_DELETE_AFTER_LAUNCH);
	}

	private static IStatus launchApdutool(String javacardHomeLocation, String arguments, String launchConfigurationName) {
		return JCToolLauncher.getLauncher(JCToolLauncher.TOOL_TYPE_APDUTOOL).launch(arguments, launchConfigurationName, false, ILauncher.DELETE_MODE_DELETE_AFTER_LAUNCH);
	}

	private static IStatus launchVerifycap(String javacardHomeLocation, String arguments, String launchConfigurationName) {
		return JCToolLauncher.getLauncher(JCToolLauncher.TOOL_TYPE_VERIFYCAP).launch(arguments, launchConfigurationName, false, ILauncher.DELETE_MODE_DELETE_AFTER_LAUNCH);
	}

	private static IStatus launchVerifyexp(String javacardHomeLocation, String arguments, String launchConfigurationName) {
		return JCToolLauncher.getLauncher(JCToolLauncher.TOOL_TYPE_VERIFYEXP).launch(arguments, launchConfigurationName, false, ILauncher.DELETE_MODE_DELETE_AFTER_LAUNCH);
	}

	private static IStatus launchJCWDE(String configFileLocation, IJavaProject[] classpathProjects, String[] options, boolean inDebugMode) {
		if (inDebugMode) {
			return JCWDELauncher.getLauncher(classpathProjects).launch(ArgumentsPreparer.prepareJCWDEArguments(configFileLocation, options), "JCWDE", ILauncher.LAUNCH_MODE_DEBUG,
					ILauncher.DELETE_MODE_DELETE_AFTER_TERMINATE);
		} else {
			return JCWDELauncher.getLauncher(classpathProjects).launch(ArgumentsPreparer.prepareJCWDEArguments(configFileLocation, options), "JCWDE", ILauncher.DELETE_MODE_DELETE_AFTER_TERMINATE);
		}
	}

	public static boolean isJavacardApplet(ICompilationUnit compilationUnit) throws JavaModelException {
		if (compilationUnit == null || !compilationUnit.exists()) {
			return false;
		}
		IType type = compilationUnit.findPrimaryType();
		if (type == null) {
			return false;
		}
		if (!type.isClass()) {
			return false;
		}
		if (Flags.isAbstract(type.getFlags())) {
			return false;
		}
		ITypeHierarchy typeHierarchy = type.newTypeHierarchy(null);
		IType[] superTypes = typeHierarchy.getAllSuperclasses(type);
		for (int i = 0; i < superTypes.length; i++) {
			if (superTypes[i].getFullyQualifiedName().equals("javacard.framework.Applet")) {
				return true;
			}
		}
		return false;
	}

	public static boolean isShareable(ICompilationUnit compilationUnit) throws JavaModelException {
		if (compilationUnit == null || !compilationUnit.exists()) {
			return false;
		}
		IType type = compilationUnit.findPrimaryType();
		if (type == null) {
			return false;
		}
		ITypeHierarchy typeHierarchy = type.newTypeHierarchy(null);
		IType[] superTypes = typeHierarchy.getAllSuperInterfaces(type);
		for (int i = 0; i < superTypes.length; i++) {
			if (superTypes[i].getFullyQualifiedName().equals("javacard.framework.Shareable")) {
				return true;
			}
		}
		return false;
	}

	public static boolean isShareableInterface(ICompilationUnit compilationUnit) throws JavaModelException {
		if (compilationUnit == null || !compilationUnit.exists()) {
			return false;
		}
		IType type = compilationUnit.findPrimaryType();
		if (type == null) {
			return false;
		}
		if (!type.isInterface()) {
			return false;
		}
		return isShareable(compilationUnit);
	}

	public static boolean isShareableClass(ICompilationUnit compilationUnit) throws JavaModelException {
		if (compilationUnit == null || !compilationUnit.exists()) {
			return false;
		}
		IType type = compilationUnit.findPrimaryType();
		if (type == null) {
			return false;
		}
		if (!type.isClass()) {
			return false;
		}
		return isShareable(compilationUnit);
	}

	public static boolean isShareableAbstractClass(ICompilationUnit compilationUnit) throws JavaModelException {
		if (compilationUnit == null || !compilationUnit.exists()) {
			return false;
		}
		IType type = compilationUnit.findPrimaryType();
		if (type == null) {
			return false;
		}
		if (!type.isClass()) {
			return false;
		}
		if (!Flags.isAbstract(type.getFlags())) {
			return false;
		}
		return isShareable(compilationUnit);
	}

	public static int getCountOfSuperShareables(ICompilationUnit compilationUnit) throws JavaModelException {
		if (compilationUnit == null || !compilationUnit.exists()) {
			return -1;
		}
		IType type = compilationUnit.findPrimaryType();
		if (type == null) {
			return -1;
		}
		int countOfSuperShareables = 0;
		ITypeHierarchy typeHierarchy = type.newTypeHierarchy(null);
		IType[] superTypes = typeHierarchy.getSupertypes(type);
		for (int i = 0; i < superTypes.length; i++) {
			if (superTypes[i].getCompilationUnit() != null && JCDECore.isShareable(superTypes[i].getCompilationUnit())) {
				countOfSuperShareables++;
			}
		}
		return countOfSuperShareables;
	}

	public static IStatus convertPackage(IPackageFragment packageFragment, String[] options) {
		try {
			String packageAID = null;
			Map<String, String> appletAIDs = new Hashtable<String, String>();
			// extracting package AID
			IStatus status = JCDECore.getPackageAID(packageFragment);
			if (status.getSeverity() == IStatus.OK) {
				packageAID = status.getMessage();
			} else {
				return status;
			}

			// extract applet AIDs
			ICompilationUnit[] comUnits = packageFragment.getCompilationUnits();
			for (int i = 0; i < comUnits.length; i++) {
				if (isJavacardApplet(comUnits[i])) {
					status = JCDECore.getAppletAID(comUnits[i]);
					if (status.getSeverity() == IStatus.OK) {
						IType type = comUnits[i].findPrimaryType();
						appletAIDs.put(type.getFullyQualifiedName(), status.getMessage());
					} else {
						return status;
					}
				}
			}

			// generate CAP
			IJavaElement packageFragmentRoot = packageFragment;
			while (packageFragmentRoot.getParent() != null && packageFragmentRoot.getElementType() != IJavaElement.PACKAGE_FRAGMENT_ROOT) {
				packageFragmentRoot = packageFragmentRoot.getParent();
			}
			String root = ResourcesPlugin.getWorkspace().getRoot().getLocation().append(packageFragment.getJavaProject().getOutputLocation()).toFile().getAbsolutePath();
			String outputRoot = packageFragment.getJavaProject().getResource().getLocation().toFile().getAbsolutePath();
			if (packageFragmentRoot != null) {
				outputRoot = ResourcesPlugin.getWorkspace().getRoot().getLocation().append(packageFragmentRoot.getPath()).toFile().getAbsolutePath();
			}
			String[] exportPath = JavaRuntime.computeDefaultRuntimeClassPath(packageFragment.getJavaProject());
			String arguments = ArgumentsPreparer.prepareConverterArguments(root, outputRoot, exportPath, packageFragment.getElementName(), packageAID, appletAIDs, "1", "0", options);
			status = JCDECore.launchConverter(getCurrentJCDKHome(), arguments, "Generate CAP (" + packageFragment.getElementName() + ")");
			if (status.getSeverity() != IStatus.OK) {
				return status;
			}

			String lastSegmentName = packageFragment.getElementName();
			if (lastSegmentName.indexOf('.') > -1) {
				lastSegmentName = lastSegmentName.substring(lastSegmentName.lastIndexOf('.') + 1);
			}
			String capFileLocation = outputRoot + File.separator + packageFragment.getElementName().replace('.', File.separatorChar) + File.separator + "javacard" + File.separator + lastSegmentName
					+ ".cap";
			return new Status(IStatus.OK, Activator.PLUGIN_ID, IStatus.OK, capFileLocation, null);
		} catch (Exception e) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, e.getMessage(), e);
		}
	}

	public static IStatus generateCAP(String jcaFileLocation, String[] options) {
		File jcaFile = new File(jcaFileLocation);
		String jcaFileName = jcaFile.getName();
		if (jcaFileName.endsWith(".jca")) {
			jcaFileName = jcaFileName.substring(0, jcaFileName.lastIndexOf(".jca"));
		}
		String capFileLocation = jcaFile.getParent() + File.separator + jcaFileName + ".cap";
		return generateCAP(jcaFileLocation, capFileLocation, options);
	}

	public static IStatus generateCAP(String jcaFileLocation, String outputFileLocation, String[] options) {
		try {
			String arguments = ArgumentsPreparer.prepareCapgenArguments(jcaFileLocation, outputFileLocation, options);
			File file = new File(jcaFileLocation);
			IStatus status = JCDECore.launchCapgen(getCurrentJCDKHome(), arguments, "Generate CAP (" + file.getName() + ")");
			if (status.getSeverity() != IStatus.OK) {
				return status;
			}
			return new Status(IStatus.OK, Activator.PLUGIN_ID, IStatus.OK, outputFileLocation, null);
		} catch (Exception e) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, e.getMessage(), e);
		}
	}

	public static IStatus verifyCAP(String[] exportFiles, String capFileLocation, String[] options) {
		try {
			String arguments = ArgumentsPreparer.prepareVerifycapArguments(exportFiles, capFileLocation, options);
			File file = new File(capFileLocation);
			return JCDECore.launchVerifycap(getCurrentJCDKHome(), arguments, "Verify CAP (" + file.getName() + ")");
		} catch (Exception e) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, e.getMessage(), e);
		}
	}

	public static IStatus verifyEXP(String expFileLocation, String[] options) {
		try {
			String arguments = ArgumentsPreparer.prepareVerifyexpArguments(expFileLocation, options);
			File file = new File(expFileLocation);
			return JCDECore.launchVerifyexp(getCurrentJCDKHome(), arguments, "Verify EXP (" + file.getName() + ")");
		} catch (Exception e) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, e.getMessage(), e);
		}
	}

	private static String powerupScript() {
		return "powerup;\n";
	}

	private static String powerdownScript() {
		return "powerdown;\n";
	}

	private static String installerScript() {
		StringBuffer script = new StringBuffer();
		script.append("// Select the installer applet\n");
		script.append("0x00 0xA4 0x04 0x00 0x09 0xa0 0x00 0x00 0x00 0x62 0x03 0x01 0x08 0x01 0x7F;\n");
		return script.toString();
	}

	private static String createAppletScript(String appletName, String appletAID) {
		String[] appletAIDArr = appletAID.split(":");
		int aidLength = appletAIDArr.length;
		int lc = aidLength + 2;
		StringBuffer createAppletAPDUCommand = new StringBuffer();
		createAppletAPDUCommand.append("// create " + appletName + " applet\n");
		createAppletAPDUCommand.append("0x80 0xB8 0x00 0x00 ");
		createAppletAPDUCommand.append("0x" + Integer.toHexString(lc) + " ");
		createAppletAPDUCommand.append("0x" + Integer.toHexString(aidLength) + " ");
		createAppletAPDUCommand.append(appletAID.replace(':', ' ') + " ");
		createAppletAPDUCommand.append("0x00 ");
		createAppletAPDUCommand.append("0x7F;\n");
		return createAppletAPDUCommand.toString();
	}

	private static String selectAppletScript(String appletName, String appletAID) {
		String[] appletAIDArr = appletAID.split(":");
		int aidLength = appletAIDArr.length;
		int lc = aidLength;
		StringBuffer createAppletAPDUCommand = new StringBuffer();
		createAppletAPDUCommand.append("// select " + appletName + " applet\n");
		createAppletAPDUCommand.append("0x00 0xA4 0x04 0x00 ");
		createAppletAPDUCommand.append("0x" + Integer.toHexString(lc) + " ");
		createAppletAPDUCommand.append(appletAID.replace(':', ' ') + " ");
		createAppletAPDUCommand.append("0x7F;\n");
		return createAppletAPDUCommand.toString();
	}

	private static String convertToString(File scriptFile) throws IOException {
		StringBuffer script = new StringBuffer();
		BufferedReader bufferedReader = new BufferedReader(new FileReader(scriptFile));
		String line = bufferedReader.readLine();
		while (line != null) {
			script.append(line + "\n");
			line = bufferedReader.readLine();
		}
		bufferedReader.close();
		return script.toString();
	}

	private static File convertToFile(String script, String outputFileLocation) throws IOException {
		File outputFile = new File(outputFileLocation);
		if (outputFile.exists()) {
			outputFile.delete();
		}
		outputFile.createNewFile();

		PrintWriter pw = new PrintWriter(outputFile);
		pw.write(script);
		pw.flush();
		pw.close();
		return outputFile;
	}

	public static IStatus generateScript(String capFileLocation, String[] options) {
		File capFile = new File(capFileLocation);
		String apduScriptFileLocation = capFile.getParent() + File.separator + "cap-download.script";
		return generateScript(capFileLocation, apduScriptFileLocation, options);
	}

	public static IStatus generateScript(String capFileLocation, String outputFileLocation, String[] options) {
		try {
			// create apdu script temp file
			File capFile = new File(capFileLocation);
			String tempFileLocation = ResourcesPlugin.getPlugin().getStateLocation().toFile().getAbsolutePath() + File.separator + "temp.script";
			File tempFile = new File(tempFileLocation);
			if (tempFile.exists()) {
				tempFile.delete();
			}
			String arguments = ArgumentsPreparer.prepareScriptgenArguments(capFileLocation, tempFileLocation, options);
			IStatus status = JCDECore.launchScriptgen(getCurrentJCDKHome(), arguments, "Generate Script (" + capFile.getName() + ")");
			if (status.getSeverity() != IStatus.OK) {
				return status;
			}

			// build script
			StringBuffer apduScript = new StringBuffer();
			apduScript.append(powerupScript());
			apduScript.append(installerScript());
			apduScript.append(convertToString(tempFile));
			apduScript.append(powerdownScript());

			convertToFile(apduScript.toString(), outputFileLocation);
			if (tempFile.exists()) {
				tempFile.delete();
			}

			return new Status(IStatus.OK, Activator.PLUGIN_ID, IStatus.OK, outputFileLocation, null);
		} catch (Exception e) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, e.getMessage(), e);
		}
	}

	public static IStatus generateCreateAppletScript(ICompilationUnit compilationUnit) {
		try {
			IStatus status = getAppletAID(compilationUnit);
			if (status.getSeverity() != IStatus.OK) {
				return status;
			}
			String appletAID = status.getMessage();
			// identify script output directory location
			String scriptDirectory = ResourcesPlugin.getWorkspace().getRoot().getLocation().append(compilationUnit.getPath()).toFile().getParent() + File.separator + "javacard";
			File scriptDirectoryFile = new File(scriptDirectory);
			if (!scriptDirectoryFile.exists()) {
				scriptDirectoryFile.mkdirs();
			}

			// build script
			String appletName = compilationUnit.getElementName();
			if (appletName.endsWith(".java")) {
				appletName = appletName.substring(0, appletName.indexOf(".java"));
			}
			StringBuffer apduScript = new StringBuffer();
			apduScript.append(powerupScript());
			apduScript.append(installerScript());
			apduScript.append(createAppletScript(appletName, appletAID));
			apduScript.append(powerdownScript());

			// set script output file location
			String outputFileLocation = scriptDirectory + File.separator + "create-" + appletName + ".script";
			convertToFile(apduScript.toString(), outputFileLocation);

			return new Status(IStatus.OK, Activator.PLUGIN_ID, IStatus.OK, outputFileLocation, null);
		} catch (Exception e) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, e.getMessage(), e);
		}
	}

	public static IStatus generateSelectAppletScript(ICompilationUnit compilationUnit) {
		try {
			IStatus status = getAppletAID(compilationUnit);
			if (status.getSeverity() != IStatus.OK) {
				return status;
			}
			String appletAID = status.getMessage();
			// identify script output directory location
			String scriptDirectory = ResourcesPlugin.getWorkspace().getRoot().getLocation().append(compilationUnit.getPath()).toFile().getParent() + File.separator + "javacard";
			File scriptDirectoryFile = new File(scriptDirectory);
			if (!scriptDirectoryFile.exists()) {
				scriptDirectoryFile.mkdirs();
			}

			// build script
			String appletName = compilationUnit.getElementName();
			if (appletName.endsWith(".java")) {
				appletName = appletName.substring(0, appletName.indexOf(".java"));
			}
			StringBuffer apduScript = new StringBuffer();
			apduScript.append(powerupScript());
			apduScript.append(selectAppletScript(appletName, appletAID));
			apduScript.append(powerdownScript());

			// set script output file location
			String outputFileLocation = scriptDirectory + File.separator + "select-" + appletName + ".script";
			convertToFile(apduScript.toString(), outputFileLocation);

			return new Status(IStatus.OK, Activator.PLUGIN_ID, IStatus.OK, outputFileLocation, null);
		} catch (Exception e) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, e.getMessage(), e);
		}
	}

	public static IStatus generateScript(IPackageFragment packageFragment, String[] converterOptions, String[] scriptgenOptions) {
		ArrayList<String> scriptsList = new ArrayList<String>();

		// generate CAP
		IStatus status = convertPackage(packageFragment, converterOptions);
		if (status.getSeverity() != IStatus.OK) {
			return status;
		}
		scriptsList.add(status.getMessage());

		// generate script
		status = generateScript(status.getMessage(), scriptgenOptions);
		if (status.getSeverity() != IStatus.OK) {
			return status;
		}
		scriptsList.add(status.getMessage());

		try {
			ICompilationUnit[] comUnits = packageFragment.getCompilationUnits();
			for (int i = 0; i < comUnits.length; i++) {
				if (isJavacardApplet(comUnits[i])) {
					status = generateCreateAppletScript(comUnits[i]);
					if (status.getSeverity() != IStatus.OK) {
						return status;
					}
					scriptsList.add(status.getMessage());
					status = generateSelectAppletScript(comUnits[i]);
					if (status.getSeverity() != IStatus.OK) {
						return status;
					}
					scriptsList.add(status.getMessage());
				}
			}
			return new Status(IStatus.OK, Activator.PLUGIN_ID, IStatus.OK, scriptsList.toString(), null);
		} catch (Exception e) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, e.getMessage(), e);
		}
	}

	public static IStatus runScript(String scriptFileLocation, String[] options) {
		try {
			File scriptFile = new File(scriptFileLocation);
			String arguments = ArgumentsPreparer.prepareApdutoolArguments(new String[] { scriptFileLocation }, options);
			return JCDECore.launchApdutool(getCurrentJCDKHome(), arguments, "Run Script (" + scriptFile.getName() + ")");
		} catch (Exception e) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, e.getMessage(), e);
		}
	}

	public static IStatus deployCAP(String capFileLocation, String[] scriptgenOptions, String[] apdutoolOptions) {
		try {
			File capFile = new File(capFileLocation);
			String scriptFileLocation = capFile.getParent() + File.separator + "cap-download.script";
			// generate script
			IStatus status = generateScript(capFileLocation, scriptFileLocation, scriptgenOptions);
			if (status.getSeverity() != IStatus.OK) {
				return status;
			}

			// run script
			return runScript(scriptFileLocation, apdutoolOptions);
		} catch (Exception e) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, e.getMessage(), e);
		}
	}

	public static IStatus deployPackage(IPackageFragment packageFragment, String[] converterOptions, String[] scriptgenOptions, String[] apdutoolOptions) {
		// generate CAP
		IStatus status = convertPackage(packageFragment, converterOptions);
		if (status.getSeverity() != IStatus.OK) {
			return status;
		}

		// generate script
		status = generateScript(status.getMessage(), scriptgenOptions);
		if (status.getSeverity() != IStatus.OK) {
			return status;
		}

		// run script
		return runScript(status.getMessage(), apdutoolOptions);
	}

	public static IStatus instantiateApplet(ICompilationUnit compilationUnit, String[] options) {
		try {
			IStatus status = generateCreateAppletScript(compilationUnit);
			if (status.getSeverity() != IStatus.OK) {
				return status;
			}
			return runScript(status.getMessage(), options);
		} catch (Exception e) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, e.getMessage(), e);
		}
	}

	public static IStatus deployJCA(String jcaFileLocation, String[] converterOptions, String[] scriptgenOptions, String[] apdutoolOptions) {
		// generate CAP
		IStatus status = generateCAP(jcaFileLocation, converterOptions);
		if (status.getSeverity() != IStatus.OK) {
			return status;
		}
		// generate script
		status = generateScript(status.getMessage(), scriptgenOptions);
		if (status.getSeverity() != IStatus.OK) {
			return status;
		}

		// run script
		return runScript(status.getMessage(), apdutoolOptions);
	}

	public static ICompilationUnit[] getWorkspaceJCApplets() throws JavaModelException {
		ArrayList<ICompilationUnit> workspaceApplets = new ArrayList<ICompilationUnit>();
		IJavaModel javaModel = JavaCore.create(ResourcesPlugin.getWorkspace().getRoot());
		IJavaProject[] javaProjects = javaModel.getJavaProjects();
		for (int i = 0; i < javaProjects.length; i++) {
			IPackageFragment[] packageFragments = javaProjects[i].getPackageFragments();
			for (int j = 0; j < packageFragments.length; j++) {
				ICompilationUnit[] compilationUnits = packageFragments[j].getCompilationUnits();
				for (int k = 0; k < compilationUnits.length; k++) {
					if (isJavacardApplet(compilationUnits[k])) {
						workspaceApplets.add(compilationUnits[k]);
					}
				}
			}
		}
		ICompilationUnit[] workspaceAppletsArr = new ICompilationUnit[workspaceApplets.size()];
		workspaceApplets.toArray(workspaceAppletsArr);
		return workspaceAppletsArr;
	}

	public static IStatus formatMethodCode(String methodCode, IJavaProject javaProject) {
		try {
			CodeFormatter codeFormatter = ToolFactory.createCodeFormatter(javaProject.getOptions(true));
			TextEdit textEdit = codeFormatter.format(CodeFormatter.K_CLASS_BODY_DECLARATIONS, methodCode, 0, methodCode.length(), 0, "\n");
			IDocument doc = new Document(methodCode);
			textEdit.apply(doc);
			return new Status(IStatus.OK, Activator.PLUGIN_ID, IStatus.OK, doc.get(), null);
		} catch (Exception e) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, e.getMessage(), e);
		}
	}

	public static IStatus formatClassCode(String classCode, IJavaProject javaProject) {
		try {
			CodeFormatter codeFormatter = ToolFactory.createCodeFormatter(javaProject.getOptions(true));
			TextEdit textEdit = codeFormatter.format(CodeFormatter.K_COMPILATION_UNIT, classCode, 0, classCode.length(), 0, "\n");
			IDocument doc = new Document(classCode);
			textEdit.apply(doc);
			return new Status(IStatus.OK, Activator.PLUGIN_ID, IStatus.OK, doc.get(), null);
		} catch (Exception e) {
			return new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, e.getMessage(), e);
		}
	}

	private static class CREFLauncherThread extends Thread {
		private String outputFileLocation;

		private String[] options;

		private IStatus status;

		private boolean terminat = false;

		private boolean terminated = false;

		public boolean isTerminated() {
			return terminated;
		}

		public IStatus getStatus() {
			return status;
		}

		public void terminate() {
			this.terminat = true;
		}

		public CREFLauncherThread(String outputFileLocation, String[] options) {
			this.outputFileLocation = outputFileLocation;
			this.options = options;
		}

		@Override
		public void run() {
			if (outputFileLocation != null && !outputFileLocation.equals("")) {
				try {
					while (!terminat) {
						File file = new File(outputFileLocation);
						status = CREFLauncher.getLauncher().launch(ArgumentsPreparer.prepareCREFArguments(outputFileLocation, options), "CREF [" + file.getName() + "]",
								ILauncher.DELETE_MODE_DELETE_AFTER_LAUNCH);
						while (!CREFLauncher.getLauncher().isTerminated() && !terminat) {
							Thread.sleep(100);
						}
					}
				} catch (InterruptedException e) {
				}
			}
			status = CREFLauncher.getLauncher().terminate();
			this.terminated = true;
		}

	}

}
